/*
 * Apache DropDownView
 * Copyright 2017 The Apache Software Foundation
 *
 * This product includes software developed at
 * The Apache Software Foundation (http://www.apache.org/).
 */

package com.anthonyfdev.dropdownview;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.app.Context;

public class DropDownView extends DependentLayout {

    private static final long COLLAPSE_TRANSITION_DURATION = 250L;
    private static final int DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS = 1;
    private static final int DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS = 0;
    private Component expandedView;
    private Component headerView;
    private ComponentContainer dropDownHeaderContainer;
    private DirectionalLayout dropDownContainer;
    private boolean isExpanded;
    private int expandedViewHeight;
    private Component emptyDropDownSpace;
    private AnimatorProperty shadowFadeOutAnimator;
    private boolean isTransitioning;
    private DropDownListener dropDownListener;
    private int backgroundColor;
    private int overlayColor;

    public DropDownView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initAttrSet(context, attrSet);
    }

    public DropDownView(Context context, AttrSet attrSet) {
        super(context, attrSet);
        initAttrSet(context, attrSet);
    }

    public DropDownView(Context context) {
        super(context);
        initAttrSet(context, null);
    }

    /**
     * @return the {@link DropDownListener} that was set by you. Default is null.
     * @see #setDropDownListener(DropDownListener)
     */
    public DropDownListener getDropDownListener() {
        return dropDownListener;
    }

    /**
     * @param dropDownListener your implementation of {@link DropDownListener}.
     * @see DropDownListener
     */
    public void setDropDownListener(DropDownListener dropDownListener) {
        this.dropDownListener = dropDownListener;
    }

    /**
     * @return true if the view is expanded, false otherwise.
     */
    public boolean isExpanded() {
        return isExpanded;
    }

    /**
     * Sets the view that will always be visible and expandable. The height of your provided view will
     * determine the height of the entire {@link DropDownView} in collapsed mode
     * (only if you set <code>wrap_content</code> to the {@link DropDownView}).
     *
     * @param headerView your header view
     */
    public void setHeaderView(Component headerView) {
        this.headerView = headerView;
        if (dropDownHeaderContainer.getChildCount() > DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS) {
            for (int i = DROP_DOWN_HEADER_CONTAINER_MIN_DEFAULT_VIEWS; i < dropDownHeaderContainer.getChildCount(); i++) {
                dropDownHeaderContainer.removeComponentAt(i);
            }
        }
        dropDownHeaderContainer.addComponent(headerView);
    }

    /**
     * Sets the view that will always be visible only when the {@link DropDownView} is in expanded mode.
     * The height of your provided view and your provided header view will determine the height of
     * the entire {@link DropDownView} in expanded mode.
     * (only if you set <code>wrap_content</code> to the {@link DropDownView}).
     *
     * @param expandedView your header view
     */
    public void setExpandedView(Component expandedView) {
        this.expandedView = expandedView;
        if (dropDownContainer.getChildCount() > DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS) {
            for (int i = DROP_DOWN_CONTAINER_MIN_DEFAULT_VIEWS; i < dropDownContainer.getChildCount(); i++) {
                dropDownContainer.removeComponentAt(i);
            }
        }
        dropDownContainer.addComponent(expandedView);
        expandedView.setVisibility(isExpanded ? Component.VISIBLE : Component.HIDE);
    }

    public void setExpandedViewHeight(int height) {
        expandedViewHeight = height;
    }

    /**
     * Animates and expands the drop down, displaying the provided expanded view. Must set expanded
     * view before this for the drop down to expand.
     */
    public void expandDropDown() {
        if (!isExpanded && !isTransitioning && expandedView != null) {
            int initHeight = headerView.getEstimatedHeight();
            dropDownContainer.setHeight(initHeight);
            beginDelayedExpandTransition();
            if (dropDownListener != null) {
                dropDownListener.onExpandDropDown();
            }
            emptyDropDownSpace.setVisibility(Component.VISIBLE);
            expandedView.setVisibility(Component.VISIBLE);
            isExpanded = true;
        }
    }

    /**
     * Animates and collapses the drop down, displaying only the provided header view. Must set expanded
     * view before this for the drop down to collapse.
     */
    public void collapseDropDown() {
        if (isExpanded && !isTransitioning && expandedView != null) {
            beginDelayedCollapseTransition();
            expandedView.setVisibility(Component.HIDE);
            if (dropDownListener != null) {
                dropDownListener.onCollapseDropDown();
            }
            isExpanded = false;
        }
    }

    private void initAttrSet(Context context, AttrSet attrs) {
        handleAttrs(context, attrs);
        LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_view_ddv_drop_down, this, true);
        bindViews();
        setupViews();
        setupTransitionSets();
    }

    private void handleAttrs(Context context, AttrSet attrs) {
        backgroundColor = attrs.getAttr("containerBackgroundColor").isPresent() ? attrs.getAttr("containerBackgroundColor").get().getIntegerValue() : 0xFF3F51B5;
        overlayColor = attrs.getAttr("overlayColor").isPresent() ? attrs.getAttr("overlayColor").get().getIntegerValue() : 0xFF3F51B5;
    }

    private void setupViews() {
        emptyDropDownSpace.setClickedListener(emptyDropDownSpaceClickListener);
        dropDownHeaderContainer.setClickedListener(dropDownHeaderClickListener);

        ShapeElement backgroundElement =  new ShapeElement();
        backgroundElement.setRgbColor(RgbColor.fromArgbInt(backgroundColor));
        dropDownContainer.setBackground(backgroundElement);
        dropDownHeaderContainer.setBackground(backgroundElement);

        ShapeElement overlayElement =  new ShapeElement();
        overlayElement.setRgbColor(RgbColor.fromArgbInt(overlayColor));
        emptyDropDownSpace.setBackground(overlayElement);
    }

    private void bindViews() {
        dropDownContainer = (DirectionalLayout) findComponentById(ResourceTable.Id_drop_down_container);
        emptyDropDownSpace = findComponentById(ResourceTable.Id_empty_drop_down_space);
        dropDownHeaderContainer = (ComponentContainer) findComponentById(ResourceTable.Id_drop_down_header);
    }

    private void beginDelayedExpandTransition() {
        int initHeight = headerView.getEstimatedHeight();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                dropDownContainer.setHeight(initHeight + (int)(v * expandedViewHeight));
            }
        });
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE);
        animatorValue.setDuration(COLLAPSE_TRANSITION_DURATION);
        animatorValue.start();
    }

    private void beginDelayedCollapseTransition() {
        int initHeight = headerView.getEstimatedHeight();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                dropDownContainer.setHeight(initHeight + expandedViewHeight - (int)(v * expandedViewHeight));
            }
        });
        animatorValue.setCurveType(Animator.CurveType.ACCELERATE);
        animatorValue.setDuration(COLLAPSE_TRANSITION_DURATION);
        animatorValue.start();

        shadowFadeOutAnimator.start();
    }

    private void setupTransitionSets() {
        shadowFadeOutAnimator = emptyDropDownSpace.createAnimatorProperty()
                .alpha(0)
                .setDuration(COLLAPSE_TRANSITION_DURATION)
                .setCurveType(Animator.CurveType.ACCELERATE)
                .setStateChangedListener(new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) {

                    }

                    @Override
                    public void onStop(Animator animator) {

                    }

                    @Override
                    public void onCancel(Animator animator) {

                    }

                    @Override
                    public void onEnd(Animator animator) {
                        emptyDropDownSpace.setVisibility(Component.HIDE);
                        emptyDropDownSpace.setAlpha(1f);
                    }

                    @Override
                    public void onPause(Animator animator) {

                    }

                    @Override
                    public void onResume(Animator animator) {

                    }
                });
    }

    private final ClickedListener dropDownHeaderClickListener = new ClickedListener() {
        @Override
        public void onClick(Component component) {
            if (isExpanded) {
                collapseDropDown();
            } else {
                expandDropDown();
            }
        }
    };

    private final ClickedListener emptyDropDownSpaceClickListener = new ClickedListener() {
        @Override
        public void onClick(Component component) {
            collapseDropDown();
        }
    };

    /**
     * A listener that wraps functionality to be performed when the drop down is successfully expanded
     * or collapsed.
     */
    public interface DropDownListener {

        void onExpandDropDown();

        void onCollapseDropDown();
    }
}
